using Sustainalytics.ESG.Entities;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Sustainalytics.Synchronization.Validation.ValidationRules.Common
{
    public abstract class Rule<T> : RuleMetadata
    {
        private Expression<Func<T, object>> property;
        private Func<T, T, bool> validator;
        private Expression<Func<T, Guid>> key;

        protected Rule()
        {
            const string DefaultString = "The Company {0} #{1}#";
            ErrorMessage = $"{DefaultString} is failing rule {this.GetType().FullName}";
        }

        public string ErrorMessage { get; private set; }

        public IEnumerable<string> Triggers { get; set; }

        public Rule<T> For(Expression<Func<T, object>> property)
        {
            this.property = property;
            return this;
        }

        public string GetErrorMessage() => $"{ErrorMessage} - {Code}";

        public Guid GetKey(T validatedObject) => key.Compile().Invoke(validatedObject);

        public bool IsValid(T validatedObject, T referenceObject)
        {
            if (validator == null)
            {
                throw new ArgumentNullException(nameof(validatedObject));
            }

            return validator.Invoke(validatedObject, referenceObject);
        }

        public object GetValue(T validatedObject)
        {
            var value = property.Compile().Invoke(validatedObject);

            if (value is ITimestamped timestamped)
                return timestamped.Value;

            return value;
        }

        public Rule<T> When(Func<T, T, bool> validator)
        {
            this.validator = validator;
            return this;
        }

        public Rule<T> WithMessage(string errorMessage)
        {
            ErrorMessage = errorMessage;
            return this;
        }

        public Rule<T> WithKey(Expression<Func<T, Guid>> keyExpression)
        {
            this.key = keyExpression;
            return this;
        }

        public Rule<T> SetTriggersMessages(IEnumerable<string> triggersMessages)
        {
            Triggers = triggersMessages;
            return this;
        }
    }
}
